/*
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
*/

using System;
using Yarn.Utility;

#nullable enable

namespace Yarn.Unity
{
    /// <summary>
    /// An interface that works with <see
    /// cref="GeneratedVariableStorageExtensions"/> to add helper methods used
    /// by generated variable storage classes.
    /// </summary>
    /// <remarks>
    /// <see cref="IVariableStorage"/> classes that implement this interface are
    /// extended with the methods found in <see
    /// cref="GeneratedVariableStorageExtensions"/>. This interface is intended
    /// to be used only with classes in source code that was generated by Yarn
    /// Spinner.
    /// </remarks>
    public interface IGeneratedVariableStorage : Yarn.IVariableStorage
    {
    }

    public static class GeneratedVariableStorageExtensions
    {
        /// <summary>
        /// Gets a value for the variable <paramref name="variableName"/> from
        /// <paramref name="storage"/>, or else returns the default value of
        /// <typeparamref name="T"/>.
        /// </summary>
        /// <typeparam name="T">The type of the parameter to get a value
        /// for.</typeparam>
        /// <param name="storage">The generated variable storage class to get
        /// the value from.</param>
        /// <param name="variableName">The name of the variable to get a value
        /// for.</param>
        /// <returns>The value of <paramref name="variableName"/>, or the
        /// default value of
        /// <typeparamref name="T"/>.</returns>
        public static T? GetValueOrDefault<T>(this IGeneratedVariableStorage storage, string variableName) where T : IConvertible
        {
            if (storage.TryGetValue<T>(variableName, out T? result))
            {
                return result;
            }
            else
            {
                UnityEngine.Debug.Log($"Failed to get a value of type {typeof(T)} for variable {variableName}.");
                return default(T);
            }
        }

        public static void SetValue<T>(this IGeneratedVariableStorage storage, string v, T value) where T : IConvertible
        {
            switch (value)
            {
                case string stringValue:

                    storage.SetValue(v, stringValue);
                    break;
                case float floatValue:
                    storage.SetValue(v, floatValue);
                    break;
                case bool boolValue:
                    storage.SetValue(v, boolValue);
                    break;
                default:
                    throw new ArgumentOutOfRangeException("Unhandled value type " + value.GetType());
            }
        }

        public static T? GetEnumValueOrDefault<T>(this IGeneratedVariableStorage storage, string variableName) where T : System.Enum
        {
            if (!storage.TryGetValue(variableName, out object? result))
            {
                UnityEngine.Debug.LogError($"Failed to get a value of type {typeof(T).Name} for variable {variableName}.");
                return default;
            }

            int caseValue;

            if (result is string stringResult)
            {
                // Convert the string value to a hash
                caseValue = (int)CRC32.GetChecksum(stringResult);
            }
            else if (result is float floatResult)
            {
                caseValue = (int)floatResult;
            }
            else if (result is int intResult)
            {
                caseValue = intResult;
            }
            else
            {
                UnityEngine.Debug.LogError($"Failed to get a value of type {typeof(T).Name} for variable {variableName}: received an unexpected variable type {result.GetType()} from variable storage");
                return default;
            }

            if (Enum.IsDefined(typeof(T), caseValue))
            {
                return (T)Enum.ToObject(typeof(T), caseValue);
            }
            else
            {
                UnityEngine.Debug.LogError($"Failed to get a value of type {typeof(T)} for variable {variableName}: ${caseValue} is not a valid case value.");
                return default;
            }
        }

    }
}
